package main

import (
	"database/sql"
	"errors"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"time"
)

var (
	srcdb  = "<user>:<password>@<protocal>[(ip:port)>]/[dbname]"
	destdb = "<user>:<password>@<protocal>[(ip:port)>]/[dbname]"

	// srcdb  = "root:key08070428@tcp(127.0.0.1:3306)/hmailserver_old"
	// destdb = "root:key08070428@tcp(127.0.0.1:3306)/hmailserver_new"
)

type account struct {
	accountid                  int
	accountdomainid            int
	accountadminlevel          int8
	accountaddress             string
	accountpassword            string
	accountactive              int8
	accountisad                int8
	accountaddomain            string
	accountadusername          string
	accountmaxsize             int
	accountvacationmessageon   int8
	accountvacationmessage     string
	accountvacationsubject     string
	accountpwencryption        int8
	accountforwardenabled      int8
	accountforwardaddress      string
	accountforwardkeeporiginal int8
	accountenablesignature     int8
	accountsignatureplaintext  string
	accountsignaturehtml       string
	accountlastlogontime       string
	accountvacationexpires     int
	accountvacationexpiredate  string
	accountpersonfirstname     string
	accountpersonlastname      string
}

type imapfolders struct {
	folderaccountid    int
	folderparentid     int
	fldername          string
	folderissubscribed int8
	foldercurrentuid   int64
}

func newImapFolders() *imapfolders {
	return &imapfolders{}
}

func clearTable(tableName string, conn *sql.DB) error {
	stmt, err := conn.Prepare("delete from " + tableName)
	if err != nil {
		return err
	}
	_, err = stmt.Exec()
	if err != nil {
		return err
	}
	return nil
}

func queryData(srcConn *sql.DB, destConn *sql.DB) error {
	// var accountData account
	rows, err := srcConn.Query(
		"select " +
			"accountid," +
			"accountdomainid," +
			"accountadminlevel," +
			"accountaddress ," +
			"accountpassword," +
			"accountactive," +
			"accountisad ," +
			"accountaddomain ," +
			"accountadusername ," +
			"accountmaxsize ," +
			"accountvacationmessageon ," +
			"accountvacationmessage," +
			"accountvacationsubject ," +
			"accountpwencryption ," +
			"accountforwardenabled ," +
			"accountforwardaddress ," +
			"accountforwardkeeporiginal ," +
			"accountenablesignature ," +
			"accountsignatureplaintext ," +
			"accountsignaturehtml ," +
			"cast(accountlastlogontime as char) as logonstr," +
			"accountvacationexpires ," +
			"cast(accountvacationexpiredate as char) as expirestr," +
			"accountpersonfirstname ," +
			"accountpersonlastname" +
			" from hm_accounts")
	if err != nil {
		return err
	}
	for rows.Next() {
		accountData := newAccount()
		// logonTime := new(time.Time)
		// accountData.accountlastlogontime = logonTime
		// expireData := new(time.Time)
		// accountData.accountvacationexpiredate = expireData
		err = rows.Scan(
			&(accountData.accountid),
			&(accountData.accountdomainid),
			&(accountData.accountadminlevel),
			&(accountData.accountaddress),
			&(accountData.accountpassword),
			&(accountData.accountactive),
			&(accountData.accountisad),
			&(accountData.accountaddomain),
			&(accountData.accountadusername),
			&(accountData.accountmaxsize),
			&(accountData.accountvacationmessageon),
			&(accountData.accountvacationmessage),
			&(accountData.accountvacationsubject),
			&(accountData.accountpwencryption),
			&(accountData.accountforwardenabled),
			&(accountData.accountforwardaddress),
			&(accountData.accountforwardkeeporiginal),
			&(accountData.accountenablesignature),
			&(accountData.accountsignatureplaintext),
			&(accountData.accountsignaturehtml),
			&(accountData.accountlastlogontime),
			&(accountData.accountvacationexpires),
			&(accountData.accountvacationexpiredate),
			&(accountData.accountpersonfirstname),
			&(accountData.accountpersonlastname),
		)
		if err != nil {
			return err
		}
		// fmt.Println(accountData)
		err = insertData(accountData, destConn)
		if err != nil {
			return err
		}
		// delete(&accountData)
	}
	err = srcConn.Close()
	if err != nil {
		return err
	}
	err = destConn.Close()
	if err != nil {
		return err
	}
	return nil
}

func packData() error {
	return nil
}

func insertImapFolder(folderaccountid int, conn *sql.DB) error {
	stmt, err := conn.Prepare("insert hm_imapfolders set " +
		"folderaccountid=?," +
		"folderparentid=-1," +
		"foldername =\"INBOX\"," +
		"folderissubscribed=1," +
		"foldercreationtime=now()," +
		"foldercurrentuid=0")
	if err != nil {
		return err
	}
	_, err = stmt.Exec(folderaccountid)
	if err != nil {
		return err
	}
	return nil
}

var ErrorStrConvertTime = errors.New("string convert to time failed!")

func insertData(accountData *account, conn *sql.DB) (err error) {
	stmt, err := conn.Prepare("insert hm_accounts set " +
		"accountdomainid = ? ," +
		"accountadminlevel = ? ," +
		"accountaddress = ? ," +
		"accountpassword = ? ," +
		"accountactive = ? ," +
		"accountisad = ? ," +
		"accountaddomain = ? ," +
		"accountadusername = ? ," +
		"accountmaxsize = ? ," +
		"accountvacationmessageon = ? ," +
		"accountvacationmessage = ? ," +
		"accountvacationsubject = ? ," +
		"accountpwencryption = ? ," +
		"accountforwardenabled = ? ," +
		"accountforwardaddress = ? ," +
		"accountforwardkeeporiginal =  ? ," +
		"accountenablesignature = ? ," +
		"accountsignatureplaintext = ? ," +
		"accountsignaturehtml = ? ," +
		"accountlastlogontime = ? ," +
		"accountvacationexpires = ? ," +
		"accountvacationexpiredate = ? ," +
		"accountpersonfirstname= ? ," +
		"accountpersonlastname = ?")
	if err != nil {
		return err
	}
	// fmt.Println(accountData.accountaddress)
	// fmt.Println(accountData.accountlastlogontime)
	logonTime, err := time.Parse("2006-01-02 15:04:05", accountData.accountlastlogontime)
	if err != nil {
		return ErrorStrConvertTime
	}
	expireTime, err := time.Parse("2006-01-02 15:04:05", accountData.accountvacationexpiredate)
	if err != nil {
		return ErrorStrConvertTime
	}
	res, err := stmt.Exec(
		accountData.accountdomainid,
		accountData.accountadminlevel,
		accountData.accountaddress,
		accountData.accountpassword,
		accountData.accountactive,
		accountData.accountisad,
		accountData.accountaddomain,
		accountData.accountadusername,
		accountData.accountmaxsize,
		accountData.accountvacationmessageon,
		accountData.accountvacationmessage,
		accountData.accountvacationsubject,
		accountData.accountpwencryption,
		accountData.accountforwardenabled,
		accountData.accountforwardaddress,
		accountData.accountforwardkeeporiginal,
		accountData.accountenablesignature,
		accountData.accountsignatureplaintext,
		accountData.accountsignaturehtml,
		// "cast('"+accountData.accountlastlogontime+"' as datetime)",
		logonTime,
		accountData.accountvacationexpires,
		// "cast('"+accountData.accountvacationexpiredate+"' as datetime)",
		expireTime,
		accountData.accountpersonfirstname,
		accountData.accountpersonlastname)
	if err != nil {
		return err
	}
	id, err := res.LastInsertId()
	if err != nil {
		return err
	}
	fmt.Printf("inserted %d data", id)
	fmt.Println()
	err = insertImapFolder(accountData.accountid, conn)
	if err != nil {
		return errors.New("create imapfolder failed!")
	}
	return nil
}

func checkData() bool {
	return true
}

func main() {
	srcConn, err := getConn(srcdb)
	checkError(err)
	destConn, err := getConn(destdb)
	checkError(err)

	//clear table hm_accounts's data
	clearTable("hm_accounts", destConn)

	//clear table hm_imapfolders's data
	clearTable("hm_imapfolders", destConn)

	err = queryData(srcConn, destConn)
	checkError(err)
	isEqual := checkData()
	if isEqual {
		errors.New("data migrate fail,please check program!")
	}
}

func newAccount() *account {
	return &account{}
}

func checkError(err error) {
	if err != nil {
		panic(err)
	}
}

func getConn(url string) (conn *sql.DB, err error) {
	conn, err = sql.Open("mysql", url)
	if err != nil {
		return
	}
	return
}
